package sql.ast;

import com.google.common.collect.Lists;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import sql.utils.Operator;
import sql.utils.Utils;

import java.util.List;
import java.util.Map;
import java.util.function.Function;

@Data
public class CompareNode extends Node {
    private String left;
    private Operator op;
    private Function<Map, List<Object>> getRight;

    public CompareNode(String left, Operator op, Function<Map, List<Object>> getRight) {
        this.left = left;
        this.op = op;
        this.getRight = getRight;
    }

    private List<Object> findInOut(List<Object> items, Map<String,Map> out) {
        if(items.size() > 1) {
            return items;
        }
        Object item = items.get(0);
        if(item instanceof String) {
            String sItem = (String) item;
            if (StringUtils.indexOf(sItem, ".") != -1) {
                String[] tableAndCol = StringUtils.split(sItem, ".");
                String table = tableAndCol[0];
                String col = tableAndCol[1];
                if (out.containsKey(table) && out.get(table).containsKey(col) && out.get(table).get(col) != null) {
                    String value = out.get(table).get(col).toString();
                    return Lists.newArrayList(value);
                } else {
                    return Lists.newArrayList();
                }
            }
        }
        return items;
    }


    @Override
    public State execute(Context context) {
        Map jsonObject = context.getRow();
        List<Object> items = this.getRight.apply(jsonObject);

        if(context.getParams() != null) {
            items = findInOut(items, context.getParams());
        }

        if(items.isEmpty()) {
            return State.False;
        }

        Object leftValue = jsonObject.get(this.getLeft());
        Object rightValue = items.get(0);
        boolean isNumComparable = leftValue instanceof Number && rightValue instanceof Number;
        Number num1 = 0;
        Number num2 = 0;
        if (isNumComparable) {
            num1 = (Number) leftValue;
            num2 = (Number) rightValue;
        }
        boolean ok;
        switch (this.op) {
            case eq:
                if(!isNumComparable) {
                    ok = leftValue != null && StringUtils.equalsIgnoreCase(rightValue.toString(), leftValue.toString());
                } else {
                    ok = Utils.compareNum(num1, num2, Operator.eq);
                }
                break;
            case ne:
                if(!isNumComparable) {
                    ok = leftValue != null && !StringUtils.equalsIgnoreCase(rightValue.toString(), leftValue.toString() );
                } else {
                    ok = Utils.compareNum(num1, num2, Operator.ne);
                }
                break;
            case in:
                ok = items.contains(leftValue);
                break;
            case lt:
                if (!isNumComparable) {
                    ok = false;
                } else {
                    ok = Utils.compareNum(num1, num2, Operator.lt);
                }
                break;
            case lte:
                if (!isNumComparable) {
                    ok = false;
                } else {
                    ok = Utils.compareNum(num1, num2, Operator.lte);
                }
                break;
            case gt:
                if (!isNumComparable) {
                    ok = false;
                } else {
                    ok = Utils.compareNum(num1, num2, Operator.gt);
                }
                break;
            case gte:
                if (!isNumComparable) {
                    ok = false;
                } else {
                    ok = Utils.compareNum(num1, num2, Operator.gte);
                }
                break;
            default:
                throw new RuntimeException("不支持该运算符，op:" + this.op);
        }
        return ok ? State.True : State.False;

    }
}
